package com.lvy.hczn.front.business.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.lvy.hczn.front.business.common.Constants;
import com.lvy.hczn.front.business.dto.SortTaskDetailDto;
import com.lvy.hczn.front.business.mapper.ProductionProcessingMapper;
import com.lvy.hczn.front.common.constant.MqConstants;
import com.lvy.hczn.front.common.core.domain.BaseModel;
import com.lvy.hczn.front.common.core.domain.entity.SysUser;
import com.lvy.hczn.front.common.utils.DictUtils;
import com.lvy.hczn.front.common.utils.ShiroUtils;
import com.lvy.hczn.front.system.domain.business.*;
import com.lvy.hczn.front.business.mapper.SortTaskDetailMapper;
import com.lvy.hczn.front.business.mapper.SortTaskMapper;
import com.lvy.hczn.front.business.service.*;
import com.lvy.hczn.front.common.exception.UtilException;
import com.lvy.hczn.front.system.domain.business.dto.*;
import com.lvy.hczn.front.system.service.IParcelService;
import com.lvy.hczn.front.system.service.SkuService;
import com.lvy.hczn.front.system.service.SpuService;
import com.lvy.hczn.front.system.service.WarehouseService;
import com.lvy.hczn.mq.rocket.ParamInfo;
import com.lvy.hczn.mq.rocket.ProducerUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.servlet.http.HttpServletRequest;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

@Slf4j
@Service
@Transactional(rollbackFor = Exception.class)
public class SortTaskDetailServiceImpl extends ServiceImpl<SortTaskDetailMapper, SortTaskDetail> implements SortTaskDetailService {

    @Autowired
    private SortTaskDetailMapper sortTaskDetailMapper;

    @Autowired
    private SortTaskMapper sortTaskMapper;

    @Autowired
    private WarehouseService warehouseService;

    @Autowired
    private GoodsBoxDetailService goodsBoxDetailService;

    @Autowired
    private ShelvesService shelvesService;

    @Autowired
    private GoodsBoxService goodsBoxService;

    @Autowired
    private SpuService spuService;
    @Autowired
    private SkuService skuService;

    @Autowired
    private IParcelService parcelService;

    @Autowired
    private SaleOrderDetailService saleOrderDetailService;

    @Autowired
    private ProducerUtil producerUtil;

    @Autowired
    private SortTaskDetailBoxService sortTaskDetailBoxService;

    @Autowired
    private WarehousingEntryService warehousingEntryService;

    @Autowired
    private SaleOrderService saleOrderService;

    @Autowired
    private SpuSortRecordService spuSortRecordService;

    @Autowired
    private MqttService mqttService;

    @Autowired
    private ProductionProcessingMapper productionProcessingMapper;

    /**
     * 根据销售单初始化分拣数据
     *
     * @param saleOrder
     * @return
     * @author yw
     * @date 2022-04-14 20:57:16
     */
    @Override
    public void initTaskDetailBySaleOrder(SaleOrder saleOrder, List<SaleOrderDetail> list, SortTask sortTask, boolean beforeSort) {
        if (saleOrder != null && !list.isEmpty()) {
            List<SortTaskDetail> detailList = new ArrayList<>();
            list.forEach(item -> {
                SortTaskDetail sortTaskDetail = new SortTaskDetail();
                sortTaskDetail.setSpuId(item.getSpuId());
                sortTaskDetail.setSkuCode(item.getSkuCode());
                sortTaskDetail.setSpecCode(item.getSpecCode());
                sortTaskDetail.setSortTaskId(sortTask.getId());
                sortTaskDetail.setSaleOrderCode(saleOrder.getSaleOrderCode());
                sortTaskDetail.setSaleOrderDetailId(item.getId());
                sortTaskDetail.setSaleOrderDetailId(item.getId());
                sortTaskDetail.setGoodsCode(item.getGoodsCode());
                sortTaskDetail.setDeliveryArea(saleOrder.getDeliveryArea());
                sortTaskDetail.setLongitude(saleOrder.getLongitude());
                sortTaskDetail.setLatitude(saleOrder.getLatitude());
                sortTaskDetail.setBatchCode(item.getBatchCode());
                sortTaskDetail.setDeliveryClerk(saleOrder.getDeliveryClerk());
                sortTaskDetail.setQuantity(item.getQuantity());
                sortTaskDetail.setUnit(item.getUnit());
                sortTaskDetail.setDetailType(com.lvy.hczn.front.common.constant.Constants.SORT_TASK_DETAIL_TYPE_ONE);
                sortTaskDetail.setSupplierCode(item.getSupplierCode());
                sortTaskDetail.setVolume(item.getVolume());
                sortTaskDetail.setWeight(item.getWeight());
//                sortTaskDetail.setStatus(MqConstants.SORT_TASK_DETAIL_WART);
                if (beforeSort) {
                    sortTaskDetail.setStatus(Constants.SORT_TASK_DETAIL_DONE);
                    sortTaskDetail.setRemark("商品分拣前退货");
                } else {
                    sortTaskDetail.setStatus(Constants.SORT_TASK_DETAIL_WART);
                }

                sortTaskDetail.setSortWeight(BigDecimal.ZERO);
                sortTaskDetail.setSortQuantity(0);
                detailList.add(sortTaskDetail);
                item.setStatus(Constants.SORT_TASK_DETAIL_ING);
            });
            if (!detailList.isEmpty()) {
                if (super.saveBatch(detailList)) {
                    saleOrderDetailService.updateBatchById(list);
                    //未分拣退货
                    if (beforeSort) {
                        sendSortDoneToPlatform(detailList.get(0), Constants.SORT_TASK_DETAIL_DONE, beforeSort);
                    } else {
                        shelvesByClerk(detailList, sortTask);
                    }
                }
            }
        }
    }

    /**
     * 定时任务生产配送员对应的货架
     *
     * @param list
     * @param sortTask
     */
    public void shelvesByClerk(List<SortTaskDetail> list, SortTask sortTask) {
        //根据配送员
        Map<String, List<SortTaskDetail>> map = list.stream().collect(Collectors.groupingBy(item -> item.getDeliveryClerk()));
        if (map != null && !map.isEmpty()) {
            List<SortTaskDetail> detailList = new ArrayList<>();
            List<Shelves> listShelves = new ArrayList<>();
            map.forEach((key, value) -> {
                generateSortTaskBySaleOrder(key, value, sortTask, detailList, listShelves);
            });
            if (!detailList.isEmpty()) {
                super.saveOrUpdateBatch(detailList);
            }
            //更新物理货架
            if (!listShelves.isEmpty()) {
                Map<String, List<Shelves>> shelvesMap = listShelves.stream().collect(Collectors.groupingBy(Shelves::getShelvesCode, LinkedHashMap::new, Collectors.toList()));
                shelvesMap.forEach((k, v) -> {
                    shelvesService.updateBatchById(v);
                });
            }
        }
    }

    /**
     * 根据销售单初始化分拣数据
     *
     * @param transferOrder
     * @return
     * @author yw
     * @date 2022-04-14 20:57:16
     */
    @Override
    public void initTaskDetailByTransfer(TransferOrder transferOrder, List<TransferOrderDetail> list, SortTask sortTask) {
        if (transferOrder != null && !list.isEmpty()) {
            List<SortTaskDetail> detailList = new ArrayList<>();
            /*获取生产加工方式*/
            ProductionProcessing processing = new ProductionProcessing();
            processing.setServiceCode(sortTask.getServiceCode());
            List<ProductionProcessing> productionProcessingList = productionProcessingMapper.selectProductionProcessingList(processing);
            LinkedHashMap<String, List<ProductionProcessing>> mapProcessing = productionProcessingList.stream().collect(Collectors.groupingBy(ProductionProcessing::getSkuCode, LinkedHashMap::new, Collectors.toList()));

            list.forEach(item -> {
                var spu = spuService.getOne(Wrappers.<Spu>lambdaQuery().eq(Spu::getSpuCode, item.getMerchandiseCode()));
                if (spu != null) {
                    List<Sku> skuList = skuService.list(Wrappers.<Sku>lambdaQuery().eq(Sku::getSpuId, spu.getId()));
                    if (!skuList.isEmpty()) {
                        Sku sku = skuList.get(0);
                        SortTaskDetail sortTaskDetail = new SortTaskDetail();
                        sortTaskDetail.setSpuId(item.getMerchandiseCode());
                        sortTaskDetail.setSkuCode(item.getMerchandiseSku());
                        sortTaskDetail.setSpecCode(item.getMerchandiseSpecif());
                        sortTaskDetail.setSortTaskId(sortTask.getId());
                        sortTaskDetail.setSaleOrderCode(transferOrder.getTransferCode());
                        sortTaskDetail.setSaleOrderDetailId(item.getId());
                        sortTaskDetail.setDeliveryClerk(transferOrder.getEnterpriseId());
                        sortTaskDetail.setGoodsCode(item.getMerchandiseCode());
                        sortTaskDetail.setBatchCode(transferOrder.getTransferCode());
                        sortTaskDetail.setGoodsName(item.getMerchandiseCode());
                        sortTaskDetail.setQuantity(transferOrder.getTransferSaleNum());
                        sortTaskDetail.setUnit(String.valueOf(transferOrder.getTransferTfNum()));
                        sortTaskDetail.setSupplierCode(item.getSupplierCode());
                        sortTaskDetail.setVolume(transferOrder.getTransferVolume());
                        sortTaskDetail.setWeight(transferOrder.getTransferWeight());
                        sortTaskDetail.setStatus(Constants.SORT_TASK_DETAIL_WART);
                        sortTaskDetail.setDetailType(com.lvy.hczn.front.common.constant.Constants.SORT_TASK_DETAIL_TYPE_TWO);
                        sortTaskDetail.setSortWeight(BigDecimal.ZERO);
                        sortTaskDetail.setSortQuantity(0);
                        //溯源码随便写个先
                        sortTaskDetail.setTraceCode(sku.getBarcodeCode());
                        sortTaskDetail.setBarCode(sku.getBarcodeCode());
                        //先设计，后面又加了总仓分拣出库吧，也算sortCode吧，也赋值，不然字段没啥用，为了跟接口统一，用sortCode字段
                        sortTaskDetail.setDeliveryArea(sortTask.getSortCode());
                        sortTaskDetail.setMaterialProcessId("0");
                        //sortTaskDetail表懒得加字段了，就这样吧，用remark代替MaterialProcessId，
                        // 再加字段，感觉有点重复，测试再看情况，分拣时MaterialProcessId为空，再校验remark
                        if (!mapProcessing.isEmpty() && mapProcessing.containsKey(sortTaskDetail.getSkuCode())) {
                            sortTaskDetail.setRemark(mapProcessing.get(sortTaskDetail.getSkuCode()).get(0).getName());
                        }
                        detailList.add(sortTaskDetail);
                    }
                }

            });
            if (!detailList.isEmpty()) {
                super.saveBatch(detailList);
            }
        }
    }

    @Override
    public void initTaskDetailByTransferParcel(TransferOrder transferOrder, List<TransferParcelDto> list, SortTask sortTask) {
        if(transferOrder != null && !list.isEmpty()) {
            List<SortTaskDetail> detailList = new ArrayList<>();
            list.forEach(item -> {
                var spu = spuService.getOne(Wrappers.<Spu>lambdaQuery().eq(Spu::getSpuCode,item.getParcelNo()));
                if(spu != null) {
                    var skuList = skuService.list(Wrappers.<Sku>lambdaQuery().eq(Sku::getSpuId,spu.getId()));
                    if(CollUtil.isNotEmpty(skuList)){
                        var sku = skuList.get(0);

                        SortTaskDetail sortTaskDetail = new SortTaskDetail();
                        sortTaskDetail.setSpuId(spu.getId());
                        sortTaskDetail.setSkuCode(sku.getId());
                        sortTaskDetail.setSpecCode("件");
                        sortTaskDetail.setSortTaskId(sortTask.getId());
                        sortTaskDetail.setSaleOrderCode(transferOrder.getTransferCode());
                        sortTaskDetail.setSaleOrderDetailId(item.getParcelNo());

                        sortTaskDetail.setGoodsCode(spu.getId());
                        sortTaskDetail.setBatchCode(transferOrder.getTransferCode());
                        sortTaskDetail.setQuantity(transferOrder.getTransferSaleNum());
                        sortTaskDetail.setUnit(String.valueOf(transferOrder.getTransferTfNum()));
                        sortTaskDetail.setSupplierCode(spu.getSupplierCode());
                        sortTaskDetail.setVolume(transferOrder.getTransferVolume());
                        sortTaskDetail.setWeight(transferOrder.getTransferWeight());
                        sortTaskDetail.setStatus(Constants.SORT_TASK_DETAIL_WART);
                        sortTaskDetail.setDetailType(com.lvy.hczn.front.common.constant.Constants.SORT_TASK_DETAIL_TYPE_THREE);
                        sortTaskDetail.setSortWeight(BigDecimal.ZERO);
                        sortTaskDetail.setSortQuantity(0);
                        detailList.add(sortTaskDetail);
                    }
                }
            });

            if (!detailList.isEmpty()) {
                super.saveBatch(detailList);
            }
        }
    }


    /**
     * 根据配送区域生成分拣任务
     *
     * @return
     * @author yw
     * @date 2022-04-14 21:30:46
     */
    @Override
    @Transactional
    public synchronized void generateSortTaskByDeliveryArea(List<SortTaskDetail> list, SortTask sortTask) {
        Warehouse warehouse = warehouseService.selectLocalWarehouseFront();
        if (list == null || list.isEmpty()) {
            list = super.list(Wrappers.<SortTaskDetail>lambdaQuery().eq(SortTaskDetail::getStatus, Constants.SORT_TASK_DETAIL_WART).eq(SortTaskDetail::getSortTaskId, "0"));
        }
        //根据配送员
        Map<String, List<SortTaskDetail>> map = list.stream().collect(Collectors.groupingBy(item -> item.getDeliveryClerk()));
        if (map != null && !map.isEmpty()) {
            List<SortTaskDetail> detailList = new ArrayList<>();
            List<Shelves> listShelves = new ArrayList<>();
            for (Map.Entry<String, List<SortTaskDetail>> entry : map.entrySet()) {
                String key = entry.getKey();
                List<SortTaskDetail> value = entry.getValue();
                if (warehouse != null) {
                    if (sortTask == null) {
                        sortTask = new SortTask();
                        sortTask.setWarehouseCode(warehouse.getWarehouseCode());
                        sortTask.setOutboundOrderCode("");
                        sortTask.setTaskType(Constants.SORT_TASK_TYPE_ORDER);
                        sortTask.setStatus(Constants.SORT_TASK_STATUS_ING);
                        sortTask.setSourceId(Constants.SORT_TASK_SOURCE_OWN);
                    }
                    sortTaskMapper.insert(sortTask);
                    generateSortTaskBySaleOrder(key, value, sortTask, detailList, listShelves);
                }
            }
            if (!detailList.isEmpty()) {
                super.saveOrUpdateBatch(detailList);
            }
            //更新物理货架
            if (!listShelves.isEmpty()) {
                Map<String, List<Shelves>> shelvesMap = listShelves.stream().collect(Collectors.groupingBy(Shelves::getShelvesCode, LinkedHashMap::new, Collectors.toList()));
                shelvesMap.forEach((k, v) -> {
                    shelvesService.updateBatchById(v);
                });
            }
        }
        /*List<GoodsBoxDetail> list1 = goodsBoxDetailService.list();
        for (GoodsBoxDetail detail : list1){
            detail.setShelfLife(LocalDateTime.now());
            try {
                Thread.sleep(1000);
                goodsBoxDetailService.updateById(detail);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }*/
    }

    /**
     * 根据订单划分货架
     * 一个分拣订单任务可以存放到多个货架
     * 不具体指定哪个分拣任务详情存放到哪个货架，由分拣员自己定
     * 配送员根据推送给他的货架编号，拿取物品
     * 若是分拣体积大于货架体积（若是货架只能放下分拣订单的一部分，也不放），则由分拣元放到空旷地方
     *
     * @param value
     * @param sortTask
     * @param detailList
     * @return void
     * @author yw
     * @date 2022-04-29 11:14:35
     */
    public void generateSortTaskBySaleOrder(String deliveryClerk, List<SortTaskDetail> value, SortTask sortTask, List<SortTaskDetail> detailList, List<Shelves> listShelves) {
        /**按订再临时货架存放**/
        Map<String, List<SortTaskDetail>> saleOrderMap = value.stream().collect(Collectors.groupingBy(it -> it.getSaleOrderCode()));
        Map<String, List> map = new HashMap<>();
        Map<String, String> mapInfo = new HashMap<>();
        saleOrderMap.forEach((k, v) -> {
            //当订单体积过大，货架放不下，存放货位则填写0，由分拣员处理
            //配送员配送订单总体积
            BigDecimal volume = value.stream().map(SortTaskDetail::getVolume).reduce(BigDecimal.ZERO, BigDecimal::add);
            //todo 后期可把订单拆解，存放到多个货架，且是拆分体积刚好合适的多个货架，若是都满足不了，则订单不拆，放地上
            //配送员已占有的货架列表
            List<Shelves> listShelvesInfo = shelvesService.list(Wrappers.<Shelves>lambdaQuery().eq(Shelves::getDeliveryClerk, deliveryClerk).eq(Shelves::getOccupied, com.lvy.hczn.front.common.constant.Constants.OCCUPIED_TRUE).gt(Shelves::getVolume, BigDecimal.ZERO).orderByAsc(Shelves::getVolume));
            //配送员已占有的货架剩余体积
            BigDecimal volumeShelvesRe = listShelvesInfo.stream().map(Shelves::getVolume).reduce(BigDecimal.ZERO, BigDecimal::add);
            List<Shelves> list = new ArrayList<>();

            //订单体积，大于配送员配分货箱的剩余体积
            if (volume.compareTo(volumeShelvesRe) > 0) {
                if (!listShelvesInfo.isEmpty()) {
                    list.addAll(listShelvesInfo);
                }
                List<Shelves> listInfo = shelvesService.list(Wrappers.<Shelves>lambdaQuery().eq(Shelves::getOccupied, com.lvy.hczn.front.common.constant.Constants.OCCUPIED_FALSE).orderByAsc(Shelves::getVolume));
                if (!listInfo.isEmpty()) {
                    list.addAll(listInfo);
                }
            } else {
                list = listShelvesInfo;
            }

            //货架总体积
            BigDecimal volumeShelves = list.stream().map(Shelves::getVolume).reduce(BigDecimal.ZERO, BigDecimal::add);

            //订单总体积小于货架剩余货位总体积，则拆分订单放到多个货架,以货架最大体积开始放
            if (volume.compareTo(volumeShelves) <= 0) {
                for (SortTaskDetail detail : v) {
                    for (Shelves item : list) {
                        if (mapInfo.containsKey(detail.getId())) {
                            break;
                        }
                        List<String> shelfLocationList = new ArrayList<>();
                        //货架体积大于等于分拣体积
                        BigDecimal subtract = item.getVolume().subtract(detail.getVolume());
                        //货架剩余存放空间，能放下分拣的商品体积
                        if (subtract.compareTo(BigDecimal.ZERO) >= 0) {
                            //获取每个分拣订单详情需要的体积，然后组装，一个分拣订单占多个货架，则每个订单都显示这个N个货架，有分拣员自己存放
                            shelfLocationList.add(item.getShelvesCode());
                            map.put(detail.getSaleOrderDetailId(), shelfLocationList);
                            mapInfo.put(detail.getId(), detail.getId());
                            detail.setSortTaskId(sortTask.getId());
                            detail.setShelfLocation(item.getShelvesCode());
                            item.setSaleOrderCode(detail.getSaleOrderCode());
                            item.setDeliveryClerk(detail.getDeliveryClerk());
                            item.setOccupied(com.lvy.hczn.front.common.constant.Constants.OCCUPIED_TRUE);
//                            initShelfForOrder(sortTask, detailList, v, item);
                            item.setVolume(subtract);
                            detailList.add(detail);
                            listShelves.add(item);
                        }
                    }
                }
            } else {
                /**货架剩余货位的总体积，小于分拣订单体积，则分拣订单需分拣员放到大点的场地，不放到货架上**/
                v.forEach(detail -> {
//                        detail.setSortTaskId(sortTask.getId());
                    detail.setShelfLocation("0");
//                        detail.setStatus(Constants.SORT_TASK_DETAIL_ING);
                    detailList.add(detail);
                });
            }
        });

        //一个分拣订单占多个货架，则每个订单都显示这个N个货架，有分拣员自己存放
        /*detailList.forEach(item -> {
            if (item.getSaleOrderDetailId().equals(map.get(item.getSaleOrderDetailId()))) {
                //显示多个货架，货位list toString到shelfLocation,[货位1，货位2]
                item.setShelfLocation(map.get(item.getSaleOrderDetailId()).toString());
            } else {
                //放不下
                item.setShelfLocation("0");
            }
        });*/
    }

    /**
     * 分拣订单指定临时货架
     *
     * @param sortTask
     * @param detailList
     * @param v
     * @param shelves
     * @return void
     * @author yw
     * @date 2022-04-23 09:51:13
     */
    private void initShelfForOrder(SortTask sortTask, List<SortTaskDetail> detailList, List<SortTaskDetail> v, Shelves shelves) {
        v.forEach(detail -> {
//            detail.setSortTaskId(sortTask.getId());
            detail.setShelfLocation(shelves.getShelvesCode());
//            detail.setStatus(Constants.SORT_TASK_DETAIL_ING);
            shelves.setSaleOrderCode(detail.getSaleOrderCode());
            shelves.setDeliveryClerk(detail.getDeliveryClerk());
            detailList.add(detail);
        });
        shelves.setOccupied(com.lvy.hczn.front.common.constant.Constants.OCCUPIED_TRUE);
//        shelvesService.updateById(shelves);
    }

    /**
     * 分拣详情生产任务
     * 按区域分拣
     * 按配送人员存放在不同的临时存放货架
     *
     * @param value
     * @param sortTask
     * @param detailList
     * @return void
     * @author yw
     * @date 2022-04-21 15:53:25
     */
    public void generateSortTaskDetail(List<SortTaskDetail> value, SortTask sortTask, List<SortTaskDetail> detailList) {
        /**同一个区域，不同的配送人员，应放在不同的临时存放位置**/
        Map<String, List<SortTaskDetail>> deliveryClerk = value.stream().collect(Collectors.groupingBy(it -> it.getDeliveryClerk()));
        deliveryClerk.forEach((k, v) -> {
            //可根据map.size一次性查，但不确定必要性，目前先这样处理
            Shelves shelves = shelvesService.getOne(Wrappers.<Shelves>lambdaQuery().eq(Shelves::getOccupied, 0).last("limit 1"));
            if (shelves != null) {
                initShelfForOrder(sortTask, detailList, v, shelves);
            } else {
                v.forEach(detail -> {
                    detail.setSortTaskId(sortTask.getId());
                    detail.setStatus(Constants.SORT_TASK_DETAIL_ING);
                    detailList.add(detail);
                });
            }
        });
    }

    /**
     * 根据分拣人员账号查询需分拣的原料列表
     *
     * @param clientId
     * @param status
     * @return
     * @author yw
     * @date 2022-04-15 19:56:35
     */
    @Override
//    @Cacheable(value = CacheConstants.SORT_TASK_DETAIL_SPU, key = "#clientId", unless = "#result.isEmpty()")
    public List<String> selectSortSpuIdByClient(String clientId, String status) throws Exception {
        if (StrUtil.isEmpty(clientId) || StrUtil.isEmpty(status)) {
            throw new UtilException("参数为空");
        }
        return sortTaskDetailMapper.selectSortSpuIdByClient(clientId, status);
    }

    /**
     * 获取分拣任务列表
     *
     * @param clientId
     * @param status
     * @return
     * @author yw
     * @date 2022-04-15 19:56:35
     */
    @Override
//    @Cacheable(value = CacheConstants.SORT_TASK_DETAIL_LIST_CLIENT, key = "#clientId", unless = "#result.isEmpty()")
    public List<SortTaskDetail> selectByClient(String clientId, String status, String spuId, String batchCode, Integer bigProduct) throws Exception {
        if (StrUtil.isEmpty(status)) {
            throw new UtilException("参数为空");
        }
        return sortTaskDetailMapper.selectByClient(clientId, status, spuId, batchCode, bigProduct);
    }

    /**
     * 累加已分拣总重量
     *
     * @param sortTaskDetail
     * @return
     * @author yw
     * @date 2022-04-18 20:16:55
     */
    @Override
    @Transactional
    public Map<String, Object> updateSortWeightById(HttpServletRequest request, SortTaskDetail sortTaskDetail, String boxCode, List<String> sortIds) throws Exception {
        Map<String, Object> map = new HashMap<>();
        log.error("称重修改分拣信息：" + JSON.toJSONString(sortTaskDetail));
        if (StrUtil.isEmpty(boxCode)) {
            throw new UtilException("参数为空");
        }
        if (sortTaskDetail.getSortWeight() == null || sortTaskDetail.getSortWeight().compareTo(BigDecimal.ZERO) <= 0) {
            throw new UtilException("分拣重量不匹配，请稍后重试");
        }
        //分拣数量
        int sortQuantity = sortTaskDetail.getSortWeight().divide(sortTaskDetail.getWeight(), 0, RoundingMode.HALF_UP).intValue();
        if (sortQuantity == 0) {
            throw new UtilException("小于一份无法分拣");
        }

        int actSortQuantity = 0;

        if (sortIds != null && !sortIds.isEmpty()) {
            for (String sortId : sortIds) {
                SortTaskDetail taskDetail = sortTaskDetailMapper.selectById(sortId);
                if ((sortQuantity + taskDetail.getSortQuantity()) > taskDetail.getQuantity()) {
                    throw new UtilException("分拣重量大于购买数量");
                }
                //是否标品
                boolean isBulk = true;
                GoodsBox goodsBox = goodsBoxService.getOne(Wrappers.<GoodsBox>lambdaQuery().eq(GoodsBox::getBoxCode, boxCode));
                if (taskDetail != null) {
                    /**更新货箱重量：若有多个，一个一个清零**/
                    List<GoodsBoxDetail> list = goodsBoxDetailService.list(Wrappers.<GoodsBoxDetail>lambdaQuery().eq(GoodsBoxDetail::getBoxCode, boxCode).eq(GoodsBoxDetail::getSpuId, taskDetail.getSpuId()).eq(GoodsBoxDetail::getSkuCode, taskDetail.getSkuCode()).eq(GoodsBoxDetail::getSpecCode, taskDetail.getSpecCode()).gt(GoodsBoxDetail::getWeight, BigDecimal.ZERO).orderByAsc(GoodsBoxDetail::getWeight));
                    /*当前按单份分拣称重，当单份超过一份的一半的时候，不分拣，如：500克，实际称重分拣750克，则不分拣**/
                    BigDecimal rangWeight = taskDetail.getWeight().multiply(Constants.HALF_PERCENT).setScale(2, RoundingMode.HALF_UP);
                    //实际分拣重量，减去应分拣重量，剩余的值，跟每份重量的一半作比较
                    BigDecimal numWeight = sortTaskDetail.getSortWeight().subtract(taskDetail.getWeight().multiply(BigDecimal.valueOf(sortQuantity))).abs();
                    //分拣重量减去每份重量*数量的值，跟每份重量的一半作比较
                    if (numWeight.compareTo(rangWeight) >= 0 || numWeight.negate().compareTo(rangWeight.negate()) <= 0) {
                        throw new UtilException("分解称重重量大于销售需分拣重量");
                    }
                    SortTask sortTask = sortTaskMapper.selectById(taskDetail.getSortTaskId());
                    //需分拣重量
                    BigDecimal sortWeight = sortTaskDetail.getSortWeight();
                    Spu spu = spuService.getById(taskDetail.getSpuId());
                    /**减去误差**/
                    BigDecimal err = BigDecimal.ZERO;
                    BigDecimal errMin = BigDecimal.ZERO;
                    if (spu != null) {
                        err = spu.getErrorMax().divide(BigDecimal.valueOf(Constants.WCS_WEIGHT_UNIT_COEFFICIENT), 2, RoundingMode.HALF_UP).setScale(2, RoundingMode.HALF_UP);
                        errMin = errMin.add(spu.getErrorMin().divide(BigDecimal.valueOf(Constants.WCS_WEIGHT_UNIT_COEFFICIENT), 2, RoundingMode.HALF_UP).setScale(2, RoundingMode.HALF_UP));
                    }
                    if (goodsBox != null) {
                        err = err.add(goodsBox.getErrorMax().divide(BigDecimal.valueOf(Constants.WCS_WEIGHT_UNIT_COEFFICIENT), 2, RoundingMode.HALF_UP).setScale(2, RoundingMode.HALF_UP));
                        errMin = errMin.add(goodsBox.getErrorMin().divide(BigDecimal.valueOf(Constants.WCS_WEIGHT_UNIT_COEFFICIENT), 2, RoundingMode.HALF_UP).setScale(2, RoundingMode.HALF_UP));
                    }

                    //误差范围内，则不分拣
                    if (sortWeight.compareTo(errMin.negate()) > 0 && sortWeight.compareTo(err) < 0) {
                        boolean isBreak = false;
                        /**若是标品，且在误差我范围内，且分拣数量小于标品每份重量的一半，则清空货箱**/
                        for (int i = 0; i < list.size(); i++) {
                            GoodsBoxDetail item = list.get(i);
                            if (sortWeight.compareTo(item.getWeightOfPerBox().divide(BigDecimal.valueOf(2), 0, RoundingMode.HALF_UP)) < 0) {
                                throw new UtilException("称重重量与销售重量不符，称重重量为：" + sortWeight);
                            }
                            item.setUpdateBy(ShiroUtils.getLoginName());
                            if (taskDetail.getSpuId().equals(item.getSpuId()) && taskDetail.getSkuCode().equals(item.getSkuCode()) && item.getWeight().compareTo(BigDecimal.ZERO) > 0) {
                                if (!item.isInBulk()) {
                                    //忘了考是虑称重负数，还是啥场景这样写了，可能改版过没在用，不确定，先留着有问题再说
                                    if (sortWeight.compareTo(sortWeight.multiply(Constants.HALF_PERCENT).setScale(2, RoundingMode.HALF_UP)) < 0) {
                                        item.setWeight(BigDecimal.ZERO);
                                        item.setVolume(BigDecimal.ZERO);
                                        goodsBox.setWeight(goodsBox.getWeight().subtract(sortWeight));
                                        if (item != null && item.getVolume().compareTo(BigDecimal.ZERO) > 0) {
                                            if (goodsBox.getVolume().subtract(item.getVolume()).compareTo(BigDecimal.ZERO) >= 0) {
                                                goodsBox.setVolume(goodsBox.getVolume().subtract(item.getVolume()));
                                            } else {
                                                goodsBox.setVolume(BigDecimal.ZERO);
                                            }
                                        }
                                        if (item.getQuantity() != null && item.getQuantity() > 0) {
                                            item.setQuantity(item.getQuantity() - 1);
                                        } else {
                                            item.setQuantity(0);
                                        }
                                        goodsBoxDetailService.updateById(item);
                                        log.error("goodsBoxService.updateById2");
                                        goodsBoxService.updateById(goodsBox);
                                        //在误差范围内的时候，不更新页面
                                        map.put("inError", true);
                                        isBreak = true;
                                        break;
                                    }
                                }
                            }
                        }
                        if (isBreak) {
                            break;
                        }
                    } else {
                        map.put("inError", false);
                    }

                    //分拣货箱记录，即从哪几个货箱中分拣商品
                    List<WarehousingEntry> sortTaskDetailBoxes = new ArrayList<>();
                    for (int i = 0; i < list.size(); i++) {
                        GoodsBoxDetail item = list.get(i);
                        item.setUpdateBy(ShiroUtils.getLoginName());
                        //spu一致且货箱还存在重量，则分拣
                        if (taskDetail.getSpuId().equals(item.getSpuId()) && taskDetail.getSkuCode().equals(item.getSkuCode()) && item.getWeight().compareTo(BigDecimal.ZERO) > 0) {
                            BigDecimal perVolume = BigDecimal.ZERO;
                            if (item.getVolume() != null && item.getWeight() != null && item.getWeight().compareTo(BigDecimal.ZERO) > 0) {
                                //单位重量的体积
                                perVolume = item.getVolume().divide(item.getWeight(), 2, RoundingMode.HALF_UP);
                            }
                            /**货箱详情重量，与分拣重量对比，货箱要么能满足重量，要么只剩余一部分，货箱spu重量小于要分拣的总量，spu重量置为0**/
                            BigDecimal weight = item.getWeight().subtract(sortWeight.subtract(BigDecimal.valueOf(actSortQuantity).multiply(sortTaskDetail.getWeight())));
                            if (!item.isInBulk()) {
                                isBulk = false;
                                /*if (item.getNumberOfPerBox() != null && item.getNumberOfPerBox() > 0) {
                                    item.setNumberOfPerBox(item.getNumberOfPerBox() - 1);
                                }*/
                            }

                            int boxSortQuantity = 0;
                            BigDecimal sortWeightInfo;
                            // 等于0，在误差范围内，大于正误差，都分拣，在误差范围内，以及等于0则清空货箱
                            final boolean flag = weight.compareTo(errMin.negate()) >= 0 && weight.compareTo(err) <= 0;
                            if (weight.compareTo(BigDecimal.ZERO) >= 0 || flag || weight.compareTo(err) > 0) {
                                if (flag && item.getWeightOfPerBox() != null && item.getWeightOfPerBox().divide(BigDecimal.valueOf(2), 2, RoundingMode.HALF_UP).compareTo(weight) > 0) {
                                    //在误差范围内，则清空货箱
                                    item.setWeight(BigDecimal.ZERO);
                                    item.setVolume(BigDecimal.ZERO);
                                }
                                if (weight.compareTo(BigDecimal.ZERO) >= 0) {
                                    item.setWeight(weight);
                                    //一箱同品类多记录，分拣超过一份的记录，需减去实际已经分拣的数量actSortQuantity
                                    boxSortQuantity = sortQuantity - actSortQuantity;
                                    sortWeightInfo = sortWeight.subtract(BigDecimal.valueOf(actSortQuantity).multiply(sortTaskDetail.getWeight()));
                                } else {
                                    boxSortQuantity = sortWeight.subtract(weight.abs()).divide(sortTaskDetail.getWeight(), 0, RoundingMode.HALF_UP).intValue();
                                    sortWeightInfo = sortWeight.subtract(weight.abs());
                                    item.setWeight(BigDecimal.ZERO);
                                }

                                //分拣体积
                                BigDecimal sortVolume = perVolume.multiply(BigDecimal.valueOf(boxSortQuantity));
                                if (item.getVolume().subtract(sortVolume).compareTo(BigDecimal.ZERO) >= 0) {
                                    item.setVolume(item.getVolume().subtract(sortVolume));
                                } else {
                                    item.setVolume(BigDecimal.ZERO);
                                }

                                taskDetail.setSortQuantity(taskDetail.getSortQuantity() + boxSortQuantity);
                                actSortQuantity = actSortQuantity + boxSortQuantity;
                                if (item.getQuantity() != null && item.getQuantity() > 0) {
                                    int detailQuantity = item.getQuantity() - boxSortQuantity;
                                    if (detailQuantity < 0) {
                                        detailQuantity = 0;
                                    }
                                    item.setQuantity(detailQuantity);
                                } else {
                                    item.setQuantity(0);
                                }
                            } else {
                                /**重量为0，则此详情置为无效，即已处理完**/
                                item.setWeight(BigDecimal.ZERO);
                                item.setVolume(BigDecimal.ZERO);
                                sortWeight = weight.abs();
                                sortWeightInfo = sortWeight;
                            }

                            sortTask.setDataType(com.lvy.hczn.front.common.constant.Constants.WAREHOUSING_ENTRY_DATA_TYPE_EIGHT);
                            if(sortTask.getAssociatedOrderCode().startsWith("BG")){
                                sortTask.setDataType("BGCK");
                                SortTask sortTask2 = new SortTask();
                                sortTask2.setId(sortTask.getId());
                                sortTask2.setDataType("BGCK");
                                sortTaskMapper.updateById(sortTask2);
                            }

                            //如果是标品，则传递分拣标品每份重量到平台
                            if (spu != null && com.lvy.hczn.front.common.constant.Constants.SPU_STANDARD_ONE.equals(spu.getStandard())) {
                                sortWeightInfo = sortTaskDetail.getWeight().multiply(BigDecimal.valueOf(boxSortQuantity));
                            }
                            initWarehousingEntry(taskDetail, item, sortWeightInfo, sortTaskDetailBoxes, sortTask, boxSortQuantity);

                            if (sortQuantity == actSortQuantity) {
                                sortWeight = BigDecimal.ZERO;
                                break;
                            }
                        }
                    }

                    goodsBoxDetailService.updateBatchById(list);

                    /**更新货箱重量**/
                    if (!list.isEmpty()) {
                        GoodsBox goodsBoxInfo = goodsBoxService.selectIncDetailByBoxCode(boxCode);
                        BigDecimal totalWeight = BigDecimal.ZERO;
                        BigDecimal totalVolume = BigDecimal.ZERO;
                        if (goodsBoxInfo != null && goodsBoxInfo.getDetailList() != null && !goodsBoxInfo.getDetailList().isEmpty()) {
                            totalWeight = goodsBoxInfo.getDetailList().stream().map(GoodsBoxDetail::getWeight).reduce(BigDecimal.ZERO, BigDecimal::add);
                            totalVolume = goodsBoxInfo.getDetailList().stream().map(GoodsBoxDetail::getVolume).reduce(BigDecimal.ZERO, BigDecimal::add);
                        }
                        LambdaUpdateWrapper<GoodsBox> updateWrapper = new LambdaUpdateWrapper<>();
                        updateWrapper.eq(GoodsBox::getBoxCode, list.get(0).getBoxCode()).set(GoodsBox::getWeight, totalWeight).set(GoodsBox::getVolume, totalVolume);
                        goodsBoxService.update(updateWrapper);
                    }

                    //当前货箱可分拣重量
                    BigDecimal finalSortWeight = sortTaskDetail.getSortWeight().subtract(sortWeight);
                    //分拣完成，需要分拣的总重量与已分拣的总重量相匹配，则分拣完成
                    BigDecimal decimal = taskDetail.getWeight().multiply(BigDecimal.valueOf(taskDetail.getQuantity())).subtract(taskDetail.getSortWeight().add(finalSortWeight));
                    //误差范围，在一份的范围之内，算是分拣完成
                    //todo 区分标品，非标品，标品不超过一份，非标品不超过百分之50？
                    log.error("updateSortWeightById:isBulk:{},taskDetail:{},finalSortWeight:{},decimal:{}", isBulk, JSON.toJSONString(taskDetail), finalSortWeight, decimal);
                    if ((!isBulk && taskDetail.getQuantity().equals(taskDetail.getSortQuantity())) || (isBulk && decimal.compareTo(taskDetail.getWeight().multiply(Constants.HALF_PERCENT)) <= 0)) {
                        taskDetail.setStatus(Constants.SORT_TASK_DETAIL_DONE);
                        if (taskDetail.getQuantity() > taskDetail.getSortQuantity()) {
                            taskDetail.setSortQuantity(taskDetail.getSortQuantity() + 1);
                            actSortQuantity = actSortQuantity + 1;
                        }
                    }
                    if (sortTaskDetail.getStatus() != null) {
                        taskDetail.setStatus(sortTaskDetail.getStatus());
                    }
//                    taskDetail.setSortWeight(taskDetail.getSortWeight().add(finalSortWeight));
                    //实际分拣重量等于分拣总重量处于分拣分数乘以实际分拣份数
                    taskDetail.setSortWeight(taskDetail.getSortWeight().add(sortTaskDetail.getSortWeight().divide(BigDecimal.valueOf(sortQuantity), 0, RoundingMode.HALF_UP).multiply(BigDecimal.valueOf(actSortQuantity))));

                    if (!sortTaskDetailBoxes.isEmpty()) {
                        warehousingEntryService.saveBatch(sortTaskDetailBoxes);
                    }

                    taskDetail.setPackId(sortTaskDetail.getPackId());
                    updateStatusById(taskDetail, sortTask);

                    map.put("quantity", actSortQuantity);

                    if (sortTaskDetail.getBigProduct() != null && !sortTaskDetail.getBigProduct().equals(com.lvy.hczn.front.common.constant.Constants.GOODS_COLLECT_YES)) {
                        int finalActSortQuantity = actSortQuantity;
                        ThreadUtil.execAsync(() -> {
                            printLabel(request, sortTask, sortTaskDetail, sortTaskDetail.getSortWeight(), taskDetail, finalActSortQuantity);
                        });
                    }
                }
            }
        }
        return map;
    }

    /**
     * @Description 打印标签
     * @Param [sortTask, sortTaskDetail, weight]
     * @Author yw
     * @Date 2024/8/1 15:16
     * @Return void
     **/
    public void printLabel(HttpServletRequest request, SortTask sortTask, SortTaskDetail sortTaskDetail, BigDecimal weight, SortTaskDetail detail, Integer quantity) {
        SaleOrder saleOrder = saleOrderService.getOne(Wrappers.<SaleOrder>lambdaQuery().eq(SaleOrder::getSaleOrderCode, sortTask.getAssociatedOrderCode()).orderByAsc(BaseModel::getCreateTime).last("limit 1"));
        if (saleOrder != null) {
            Map<String, Object> map = new HashMap<>();
            String topic = "sort_weight_topic";
            map.put("topic", topic);
            String ipString = request.getRemoteAddr();
//            ipString = "192.168.0.109";
            map.put("tag", ipString);
            Map<String, Object> mapData = new HashMap<>();
            mapData.put("userName", saleOrder.getName());
            mapData.put("phone", saleOrder.getPhone());
            mapData.put("goodsName", detail.getGoodsCode());
            mapData.put("weight", weight);
            mapData.put("perWeight", detail.getWeight());
            mapData.put("quantity", quantity);
            mapData.put("qrCode", 0);
            mapData.put("goodsCode", detail.getSkuCode());
            mapData.put("standard", sortTaskDetail.getStandard());
            mapData.put("url", 0);
            mapData.put("price", 0);
            map.put("data", mapData);
            String jsonStr = JSON.toJSONString(map);
            log.error("打印参数：{}", jsonStr);
            mqttService.sendToMqtt(topic, jsonStr);
        }
    }

    /**
     * @Description 地平仓分拣，修改库存，可以用货架分拣updateSortWeightById逻辑，懒得改和，懒得测试
     * @Param
     * @Author yw
     * @Date 2024/6/20 11:26
     * @Return
     **/
    @Override
    public Map<String, Object> updateSortWeightByIdOfLarge(HttpServletRequest request, SortTaskDetailDto sortTaskDetail) throws Exception {
        Map<String, Object> map = new HashMap<>();
        //分拣数量
        int sortQuantity = sortTaskDetail.getSortWeight().divide(sortTaskDetail.getWeight(), 0, RoundingMode.HALF_UP).intValue();
        if (sortQuantity == 0) {
            throw new UtilException("小于一份无法分拣");
        }

        String vir = DictUtils.getDictValue(com.lvy.hczn.front.common.constant.Constants.VIRTUALLY_BOX_GOODS, com.lvy.hczn.front.common.constant.Constants.VIRTUALLY_BOX_GOODS_BOX_CODE);
        if (StrUtil.isEmpty(vir)) {
            throw new UtilException("地平仓货箱参数未设置");
        }

        BigDecimal sortWeight = sortTaskDetail.getSortWeight();
        SortTaskDetail detail = new SortTaskDetail();
        if (sortTaskDetail.getSortIds() != null && !sortTaskDetail.getSortIds().isEmpty()) {
            //只会循环一次
            for (String sortId : sortTaskDetail.getSortIds()) {
                SortTaskDetail taskDetail = sortTaskDetailMapper.selectById(sortId);
                detail = taskDetail;
                List<GoodsBoxDetail> goodsBoxDetailList = goodsBoxDetailService.list(Wrappers.<GoodsBoxDetail>lambdaQuery().eq(GoodsBoxDetail::getSpuId, taskDetail.getSpuId()).eq(GoodsBoxDetail::getSkuCode, taskDetail.getSkuCode()).eq(GoodsBoxDetail::getSpecCode, taskDetail.getSpecCode()).eq(GoodsBoxDetail::getSupplierCode, taskDetail.getSupplierCode()).gt(GoodsBoxDetail::getWeight, BigDecimal.ZERO));
                BigDecimal totalWeight = goodsBoxDetailList.stream().map(GoodsBoxDetail::getWeight).reduce(BigDecimal.ZERO, BigDecimal::add);
                if (totalWeight.compareTo(BigDecimal.ZERO) <= 0) {
                    throw new UtilException("仓库库存为空，可进行缺货处理");
                }
            }
        }

        //每份重量
        BigDecimal perOfSortWeight = sortTaskDetail.getSortWeight().divide(BigDecimal.valueOf(sortQuantity), 0, RoundingMode.HALF_UP);
        for (int i = 0; i < sortQuantity; i++) {
            sortTaskDetail.setSortWeight(perOfSortWeight);
            map = updateSortWeightById(request, sortTaskDetail, sortTaskDetail.getBoxCode(), sortTaskDetail.getSortIds());
        }

        GoodsBoxDetail goodsBoxDetail = new GoodsBoxDetail();
        goodsBoxDetail.setBoxCode(vir);
        goodsBoxDetail.setSkuCode(detail.getSkuCode());
        goodsBoxDetail.setSpecCode(detail.getSpecCode());
        goodsBoxDetailService.selectBoxDetailByBoxCode(goodsBoxDetail);

        SortTask sortTask = sortTaskMapper.selectById(detail.getSortTaskId());
        printLabel(request, sortTask, sortTaskDetail, sortWeight, detail, sortQuantity);
        map.put("quantity", sortQuantity);
        return map;
    }

    /**
     * 分拣生成出库单
     *
     * @param sortTaskDetail
     * @param goodsBoxDetail
     * @param sortWeight
     * @param sortTaskDetailBoxes
     * @return void
     * @author yw
     * @date 2023-08-18 11:43:02
     */
    @Override
    public void initWarehousingEntry(SortTaskDetail sortTaskDetail, GoodsBoxDetail goodsBoxDetail, BigDecimal sortWeight, List<WarehousingEntry> sortTaskDetailBoxes, SortTask sortTask, int quantity) {
        if (sortTask != null) {
            WarehousingEntry warehousingEntry = new WarehousingEntry();
            warehousingEntry.setEntryCode(sortTask.getAssociatedOrderCode());
            warehousingEntry.setServiceCode(sortTask.getServiceCode());
            warehousingEntry.setSourceId(sortTask.getSourceId());
            warehousingEntry.setDataType(sortTask.getDataType());
            /*if (quantity == 0) {
                //仓库缺货
                warehousingEntry.setDataType(com.lvy.hczn.front.common.constant.Constants.WAREHOUSING_ENTRY_DATA_TYPE_TEN);
            } else {
                warehousingEntry.setDataType(sortTask.getDataType());
            }*/
            warehousingEntry.setWarehouseCode(sortTask.getWarehouseCode());
            warehousingEntry.setSpuId(sortTaskDetail.getSpuId());
            warehousingEntry.setSkuCode(sortTaskDetail.getSkuCode());
            warehousingEntry.setSpecCode(sortTaskDetail.getSpecCode());
            warehousingEntry.setUnit(sortTaskDetail.getUnit());
            warehousingEntry.setSupplierCode(sortTaskDetail.getSupplierCode());
            warehousingEntry.setMemberCode(sortTask.getPublisher());

            if (goodsBoxDetail != null) {
                warehousingEntry.setBoxCode(goodsBoxDetail.getBoxCode());
                //仓库采购入库的批次
                warehousingEntry.setRemark(goodsBoxDetail.getPurchaseOrderCode());
                warehousingEntry.setPickTime(goodsBoxDetail.getProduceDate());
                warehousingEntry.setWarrantyDate(goodsBoxDetail.getWarrantyDate());
                if (goodsBoxDetail.getProduceDate() != null && goodsBoxDetail.getWarrantyDate() != null) {
                    warehousingEntry.setShelfLife(goodsBoxDetail.getProduceDate().plusDays(goodsBoxDetail.getWarrantyDate()));
                }
                warehousingEntry.setProducerId(goodsBoxDetail.getProducerId());
            } else {
                warehousingEntryService.initBaseData(warehousingEntry, null);
            }
            warehousingEntry.setBatchCode(sortTask.getAssociatedOrderCode());
            warehousingEntry.setWeightOfPerBox(sortTaskDetail.getWeight());
            warehousingEntry.setWeight(sortWeight);
            warehousingEntry.setQuantity(quantity);

            warehousingEntry.setStatus(Constants.ENTRY_ING);

            warehousingEntry.setUpdateBy(sortTaskDetail.getUpdateBy());
            warehousingEntry.setSaleOrderDetailId(sortTaskDetail.getSaleOrderDetailId());
            warehousingEntry.setFullBox(1); // todo: 暂时都是非整箱

            sortTaskDetailBoxes.add(warehousingEntry);
        }
    }

    /**
     * 根据ID更改状态（如缺货），且校验有没分拣完成，分拣完成更改父表信息
     *
     * @param sortTaskDetail
     * @return
     * @author yw
     * @date 2022-04-18 20:16:55
     */
    @Override
    @Transactional
    public void updateStatusById(SortTaskDetail sortTaskDetail, SortTask sortTask) throws Exception {
        if (sortTaskDetail == null) {
            throw new UtilException("无参数信息");
        }
        if (!StrUtil.isAllNotEmpty(sortTaskDetail.getId(), sortTaskDetail.getStatus())) {
            throw new UtilException("参数为空");
        }
        //分拣时若还有商品，且非到临期出库，不能缺货
        if (Constants.SORT_TASK_SORT_OWE.equals(sortTaskDetail.getStatus())) {
            SortTaskDetail sortTaskDetailInfo = sortTaskDetailMapper.selectById(sortTaskDetail.getId());
            if (sortTaskDetailInfo != null) {
                //会存在分拣一份的情况，所以更新重量
                sortTaskDetail.setSortWeight(sortTaskDetailInfo.getSortWeight());
                //查询最近临期的商品的生产日期，如果仓库内还有大于此生产日期的商品，说明仓库还有未临期的库存库存
                SpuSortRecord spuSortRecordInfo = spuSortRecordService.selectSortFrmLossBySpu(sortTaskDetailInfo);

                LambdaQueryWrapper<GoodsBoxDetail> lambdaQueryWrapper = new LambdaQueryWrapper<>();
                lambdaQueryWrapper.eq(GoodsBoxDetail::getSpuId, sortTaskDetailInfo.getSpuId());
                lambdaQueryWrapper.eq(GoodsBoxDetail::getSkuCode, sortTaskDetailInfo.getSkuCode());
                lambdaQueryWrapper.gt(GoodsBoxDetail::getWeight, BigDecimal.ZERO);
                if (spuSortRecordInfo != null) {
                    lambdaQueryWrapper.ge(GoodsBoxDetail::getShelfLife, spuSortRecordInfo.getShelfLife());
                }
                List<GoodsBoxDetail> goodsBoxDetailList = goodsBoxDetailService.list(lambdaQueryWrapper);
                if (goodsBoxDetailList != null && !goodsBoxDetailList.isEmpty()) {
                    throw new UtilException("仓库存在该商品，无需缺货");
                }
            }
        }
        if (sortTaskDetailMapper.updateById(sortTaskDetail) == 1) {
            if (sortTask == null) {
                sortTask = sortTaskMapper.selectById(sortTaskDetail.getSortTaskId());
                sortTask.setDataType(com.lvy.hczn.front.common.constant.Constants.WAREHOUSING_ENTRY_DATA_TYPE_ELEVEN);
            }
            if (Constants.SORT_TASK_SORT_OWE.equals(sortTaskDetail.getStatus())) {
                sortLessThan(sortTaskDetail, sortTask);
            }
            //校验分拣完成则发送入库完成通知
            List<SortTaskDetail> sortTaskDetails = super.list(Wrappers.<SortTaskDetail>lambdaQuery().eq(SortTaskDetail::getSortTaskId, sortTaskDetail.getSortTaskId()).in(SortTaskDetail::getStatus, Constants.SORT_TASK_DETAIL_WART, Constants.SORT_TASK_DETAIL_ING));
            if (sortTaskDetails == null || sortTaskDetails.isEmpty()) {
                sortTask.setStatus(Constants.SORT_TASK_STATUS_DONE);
                if (sortTaskMapper.updateById(sortTask) == 1) {
                    if (com.lvy.hczn.front.common.constant.Constants.WAREHOUSING_ENTRY_DATA_TYPE_EIGHT.equals(sortTask.getDataType())
                            || com.lvy.hczn.front.common.constant.Constants.WAREHOUSING_ENTRY_DATA_TYPE_TEN.equals(sortTask.getDataType())
                            || com.lvy.hczn.front.common.constant.Constants.WAREHOUSING_ENTRY_DATA_TYPE_ELEVEN.equals(sortTask.getDataType())
                            || "BGCK".equals(sortTask.getDataType())) {

                        log.error("销售出库单发货出库分拣完成");
                        sendSortDoneToPlatform(sortTaskDetail, Constants.SORT_TASK_DETAIL_DONE, false);
                        ThreadUtil.execAsync(() -> {
                            printReceipt(sortTaskDetail);
                        });
                    } else if (com.lvy.hczn.front.common.constant.Constants.WAREHOUSING_ENTRY_DATA_TYPE_NINE.equals(sortTask.getDataType())) {
                        //调拨出库分拣完成通知
                        log.error("调拨出库");
                        log.error("分拣出库传输到：{}",sortTaskDetail.getSaleOrderDetailId());
//                        String bgPrefix = DictUtils.getDictValue(com.lvy.hczn.front.common.constant.Constants.SYS_DICT_ORDER_TYPE, com.lvy.hczn.front.common.constant.Constants.SYS_DICT_ORDER_TYPE_BG);
                        if(sortTaskDetail.getSaleOrderDetailId().startsWith("BG")){
                            // 包裹出库同步到平台
                            sendSortParcelTransferDoneToPlatform(sortTaskDetail, sortTask);
                            return;
                        }
                        sendSortTransferDoneToPlatform(sortTaskDetail, Constants.SORT_TASK_DETAIL_DONE, sortTask);
                    }
                }
            }
        }
    }

    /**
     * 调拨出库分拣完成通知
     *
     * @param sortTaskDetail
     * @param status
     * @return void
     * @author yw
     * @date 2023-08-25 19:54:00
     */
    @Override
    public void sendSortTransferDoneToPlatform(SortTaskDetail sortTaskDetail, String status, SortTask sortTask) {
        var warehousingEntrys = warehousingEntryService.selectByEntryCodeAndStatusList(sortTask.getAssociatedOrderCode(),Constants.ENTRY_ED);

        if(CollUtil.isNotEmpty(warehousingEntrys)){
            ParamInfo<WarehousingEntry> info = new ParamInfo<>();
            info.setWarehouseCode(sortTask.getWarehouseCode());
            info.setWarehouseEntryCode(sortTask.getAssociatedOrderCode());
            info.setList(warehousingEntrys);
            log.error("sendSortTransferDoneToPlatform:{}", JSON.toJSONString(info));
            try {
                producerUtil.sendAsyncMsg(MqConstants.TOPIC_TRANSFER_MSG, null, JSON.toJSONString(info).getBytes(StandardCharsets.UTF_8), IdUtil.fastSimpleUUID());
            } catch (Exception e) {
                throw new UtilException(e);
            }
        }

//        GoodsBox goodsBox = new GoodsBox();
//        goodsBox.setOutboundOrderCode(sortTask.getAssociatedOrderCode());
//        List<GoodsBox> list = goodsBoxService.selectTransferSortDoneByOutBoundOrderCode(goodsBox);
//        if (!list.isEmpty()) {
//            Warehouse warehouse = warehouseService.selectLocalWarehouseFront();
//
//
//
//
//        }
    }

    private void sendSortParcelTransferDoneToPlatform(SortTaskDetail sortTaskDetail, SortTask sortTask) {
        String bgPrefix = DictUtils.getDictValue(com.lvy.hczn.front.common.constant.Constants.SYS_DICT_ORDER_TYPE, com.lvy.hczn.front.common.constant.Constants.SYS_DICT_ORDER_TYPE_BG);
        if(sortTaskDetail.getSaleOrderDetailId().startsWith(bgPrefix)){
            log.debug("包裹出库调拨完成");

            WarehouseParcelDto warehouseParcelDto = new WarehouseParcelDto();
            warehouseParcelDto.setWarehouseCode(sortTask.getWarehouseCode());
            warehouseParcelDto.setTicketType("BGCK");
            warehouseParcelDto.setSortDate(LocalDateTime.now());
            List<WarehouseParcelDetailDto> warehouseParcelDetailDtos = new ArrayList<>();

            Parcel parcel = parcelService.getOne(Wrappers.<Parcel>lambdaQuery().eq(Parcel::getParcelNo, sortTaskDetail.getSaleOrderDetailId()));

            List<ParcelDetailDto> parcelList = JSONUtil.toList(parcel.getParcelDetail(), ParcelDetailDto.class);

            for (ParcelDetailDto parcelDetailDto : parcelList) {
                WarehouseParcelDetailDto detailDto = new WarehouseParcelDetailDto();

                detailDto.setParcelNo(parcel.getParcelNo());
                detailDto.setCartonCode(parcelDetailDto.getCartonCode());
                detailDto.setTemperId(parcelDetailDto.getTemperId());
                detailDto.setContainerId(parcelDetailDto.getContainerId());
                detailDto.setCargoId(parcelDetailDto.getCargoId());
                detailDto.setWeight(parcelDetailDto.getWeight());
                detailDto.setVolume(parcelDetailDto.getVolume());
                warehouseParcelDetailDtos.add(detailDto);
            }

            warehouseParcelDto.setList(warehouseParcelDetailDtos);

//            ParamInfo<WarehouseParcelDto> paramInfo = new ParamInfo<>();
//            paramInfo.setWarehouseCode(sortTask.getWarehouseCode());
//            paramInfo.setWarehouseEntryCode(sortTask.getAssociatedOrderCode());
//            paramInfo.setData(warehouseParcelDto);

            // 发送消息到数据转发中心
            try {
                producerUtil.sendAsyncMsg(MqConstants.TOPIC_ARRIVE, MqConstants.TOPIC_ARRIVE_TAG_PARCEL_ENTRY, JSONUtil.toJsonStr(warehouseParcelDto).getBytes(StandardCharsets.UTF_8), IdUtil.fastSimpleUUID());
            } catch (Exception e) {
                throw new UtilException(e);
            }
        }
    }


    /**
     * @Description 订单分拣完成打印小票
     * @Param
     * @Author yw
     * @Date 2024/8/26 15:16
     * @Return
     **/
    @Override
    public void printReceipt(SortTaskDetail sortTaskDetail) {
        //异步打印，分拣单据还没更新成功，延时3秒再查询打印
        ThreadUtil.sleep(3000);
        List<SortTaskDetail> sortTaskDetails = sortTaskDetailMapper.selectList(Wrappers.<SortTaskDetail>lambdaQuery().eq(SortTaskDetail::getSortTaskId, sortTaskDetail.getSortTaskId()));
        if (sortTaskDetails != null && !sortTaskDetails.isEmpty()) {
            SaleOrder saleOrder = saleOrderService.getOne(Wrappers.<SaleOrder>lambdaQuery().eq(SaleOrder::getSaleOrderCode, sortTaskDetail.getSaleOrderCode()).orderByAsc(BaseModel::getCreateTime).last("limit 1"));
            if (saleOrder != null) {
                Map<String, Object> map = new HashMap<>();
                String sn = DictUtils.getDictValue(com.lvy.hczn.front.common.constant.Constants.SYS_DICT_PRINTER, com.lvy.hczn.front.common.constant.Constants.SYS_DICT_PRINTER_SN);
                if (!StrUtil.isEmpty(sn)) {
                    map.put("userName", saleOrder.getName());
                    map.put("phone", saleOrder.getPhone());
                    map.put("printerNo",sn);
                    map.put("orderNo",saleOrder.getServiceCode());
                    List<Map<String, Object>> mapList = new ArrayList<>();
                    sortTaskDetails.forEach(item -> {
                        if (item.getSortQuantity() > 0) {
                            Map<String, Object> objectMap = new HashMap<>();
                            objectMap.put("name", item.getGoodsCode());
                            objectMap.put("perWeight", item.getWeight());
                            objectMap.put("totalWeight", item.getSortWeight());
                            objectMap.put("num", item.getSortQuantity());
                            mapList.add(objectMap);
                        }
                    });
                    if (!mapList.isEmpty()) {
                        Map<String,Object> param = new HashMap<>();
                        map.put("list", mapList);
                        param.put("data",map);
                        String topic = "receipt_topic";
                        param.put("topic",topic);
                        mqttService.sendToMqtt(topic, JSON.toJSONString(param));
                    }
                } else {
                    log.error("打印机SN编号为空");
                }
            } else {
                log.error("printReceipt销售单为空");
            }
        }
    }

    /**
     * 缺货推送平台
     *
     * @param sortTaskDetail
     * @return void
     * @author yw
     * @date 2023-09-17 14:19:34
     */
    public void sortLessThan(SortTaskDetail sortTaskDetail, SortTask sortTask) {
        List<WarehousingEntry> sortTaskDetailBoxes = new ArrayList<>();

        SortTaskDetail detail = super.getById(sortTaskDetail.getId());
        if (detail.getQuantity() != null && detail.getSortQuantity() != null) {
            int lessNum = detail.getQuantity() - detail.getSortQuantity();
            for (int i = 0; i < lessNum; i++) {
                initWarehousingEntry(sortTaskDetail, null, detail.getWeight(), sortTaskDetailBoxes, sortTask, 1);
            }

            if (!sortTaskDetailBoxes.isEmpty()) {
                if (warehousingEntryService.saveBatch(sortTaskDetailBoxes)) {
                /*WarehousingEntry warehousingEntry = new WarehousingEntry();
                warehousingEntry.setEntryCode(sortTask.getAssociatedOrderCode());
                warehousingEntry.setServiceCode(sortTask.getServiceCode());
                log.error("缺货推送平台：{}", JSON.toJSONString(sortTaskDetail));
                warehousingEntryService.commitToPlatform(warehousingEntry);*/
                }
            }
        }
    }

    /**
     * 销售配送分拣完成通知，发送到平台
     *
     * @param sortTaskDetail
     * @param status
     * @return void
     * @author yw
     * @date 2023-06-27 09:15:15
     */
    public void sendSortDoneToPlatform(SortTaskDetail sortTaskDetail, String status, boolean beforeSort) {
        sortTaskDetail.setStatus(status);
        List<SortTaskDetail> sortTaskDetails = super.list(Wrappers.<SortTaskDetail>lambdaQuery().eq(SortTaskDetail::getSortTaskId, sortTaskDetail.getSortTaskId()).notIn(SortTaskDetail::getStatus, Constants.SORT_TASK_DETAIL_WART, Constants.SORT_TASK_DETAIL_ING));
        String name;
        if (!beforeSort) {
            SysUser sysUser = ShiroUtils.getSysUser();
            if (sysUser != null) {
                name = sysUser.getUserName();
            } else {
                name = "";
            }
        } else {
            name = "wms";
        }
        //先这样处理，以后再说
        if (!sortTaskDetails.isEmpty()) {
            sortTaskDetails.forEach(detail -> {
                Spu spu = spuService.getById(detail.getSpuId());
                if (spu != null) {
                    detail.setSpuCode(spu.getSpuCode());
                    detail.setStandard(spu.getStandard());
                }
                detail.setUpdateBy(name);
            });
            SortTask sortTask = sortTaskMapper.selectById(sortTaskDetail.getSortTaskId());
            if (sortTask != null) {
                /**更新销售订单**/
                LambdaUpdateWrapper<SaleOrder> updateWrapper = new LambdaUpdateWrapper<>();
                updateWrapper.eq(SaleOrder::getSaleOrderCode, sortTask.getAssociatedOrderCode());
                updateWrapper.eq(SaleOrder::getType, com.lvy.hczn.front.common.constant.Constants.SALE_ORDER_TYPE_ONE);
                updateWrapper.set(SaleOrder::getStatus, com.lvy.hczn.front.common.constant.Constants.SALE_ORDER_DETAIL_STATUS_TWO);
                saleOrderService.update(updateWrapper);

                if (!beforeSort) {
                    sortTask.setList(sortTaskDetails);
                    ParamInfo<SortTask> paramInfo = new ParamInfo<>();
                    paramInfo.setData(sortTask);
                    try {
                        log.error("销售配送分拣完成通知{}", JSON.toJSONString(paramInfo));
                        producerUtil.sendAsyncMsg(MqConstants.TOPIC_SORT_DONE, null, JSON.toJSONString(paramInfo).getBytes(StandardCharsets.UTF_8), IdUtil.fastSimpleUUID());
                    } catch (Exception e) {
                        throw new UtilException(e);
                    }
                }

                WarehousingEntry warehousingEntry = new WarehousingEntry();
                warehousingEntry.setEntryCode(sortTask.getAssociatedOrderCode());
                warehousingEntry.setServiceCode(sortTask.getServiceCode());
                log.error("销售配送分拣完成推送出库单{}", JSON.toJSONString(warehousingEntry));
                warehousingEntryService.commitToPlatform(warehousingEntry, true, false);
            }
        }
    }


}